// SPDX-License-Identifier: GPL-2.0
/*
 * RECLAIM: RECLAIM AN SHARED MEMORY
 *
 * (C) 2024.05.01 BuddyZhang1 <buddy.zhang@aliyun.com>
 */
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/swap.h>

#define SPECIAL_DEV_NAME	"BiscuitOS-RECLAIM"
#define BISCUITOS_IO		0xBD
#define BS_RECLAIM		_IO(BISCUITOS_IO, 0x00)
#define BS_RECLAIM_PREPARE	_IO(BISCUITOS_IO, 0x01)
#define BS_RECLAIM_PAGES	0x100000

static int BiscuitOS_pte(pte_t *pte, unsigned long addr, void *data)
{
	struct page *page;

	page = pte_page(*pte);

	/* FORCE PAGE INTO LRU */
	lru_add_drain();
	/* MARK LATEST UNUSED */
	folio_clear_referenced(page_folio(page));
	set_pte_at(current->mm, addr, pte, pte_mkold(*pte));

	if (!folio_test_lru(page_folio(page))) {
		printk("PAGE NOT IN LRU\n");
		return -EINVAL;
	} else
		printk("TARGET PFN %#lx\n", page_to_pfn(page));

	return 0;
}

static int BiscuitOS_reclaim_prepare(unsigned long addr)
{
	return apply_to_page_range(current->mm, addr, PAGE_SIZE,
			BiscuitOS_pte, NULL);
}

static int BiscuitOS_reclaim(unsigned long addr)
{
	/* TRIGGER RECLAIM */
	shrink_all_memory(BS_RECLAIM_PAGES);

	return 0;
}

static long BiscuitOS_ioctl(struct file *filp,
                        unsigned int ioctl, unsigned long arg)
{
	switch (ioctl) {
	case BS_RECLAIM:
		BiscuitOS_reclaim(arg);
		break;
	case BS_RECLAIM_PREPARE:
		BiscuitOS_reclaim_prepare(arg);
		break;
	}
	return 0;
}

static struct file_operations BiscuitOS_fops = {
	.owner		= THIS_MODULE,
	.unlocked_ioctl	= BiscuitOS_ioctl,
};

static struct miscdevice BiscuitOS_drv = {
	.minor	= MISC_DYNAMIC_MINOR,
	.name	= SPECIAL_DEV_NAME,
	.fops	= &BiscuitOS_fops,
};

static int __init BiscuitOS_init(void)
{
	return misc_register(&BiscuitOS_drv);
}
device_initcall(BiscuitOS_init);
